/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.dao.impl;

import java.awt.geom.Point2D;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;
import org.jdom.Attribute;
import org.jdom.DataConversionException;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;

import who.tmap.bo.Annotations;
import who.tmap.bo.BizObject;
import who.tmap.bo.BizObjects;
import who.tmap.bo.TextStyle;
import who.tmap.bo.impl.BizObjectFactory;
import who.tmap.common.TMapException;
import who.tmap.dao.AnnotationsDAO;
import who.tmap.dao.TMapDAOException;
import who.tmap.toolkit.dao.LabelsListDAO;
import who.tmap.toolkit.dao.MapShapeDAO;
import who.tmap.toolkit.dao.MapShapeDAOFactory;
import who.tmap.toolkit.shape.LabelShape;
import who.tmap.toolkit.shape.MapShape;
import who.tmap.toolkit.shape.MapShapes;
import who.tmap.toolkit.shape.annotation.AnnotationShape;
import who.tmap.toolkit.shape.annotation.LabelsListShape;
import who.tmap.toolkit.shape.annotation.LegendShape;
import who.tmap.toolkit.shape.annotation.TitleShape;
import who.tmap.toolkit.shape.impl.MapShapeFactory;
import who.tmap.util.MiscHelper;

/**
 * DAO for Annotations
 * 
 * @author stl
 *
 */
public class AnnotationsDAOImpl extends BaseDAOImpl implements AnnotationsDAO {

    private static final String VERSION ="1.0"; 
    protected MapShapeDAOFactory msdf =MapShapeDAOFactory.getInstance(); 
    protected MapShapeFactory msf =MapShapeFactory.getInstance();
    private MiscHelper miscHelper =new MiscHelper();
    
    private static Logger logger = Logger.getLogger(AnnotationsDAOImpl.class);      
    
    /**
     *	Package visible only (enough for the factory)
     */
    AnnotationsDAOImpl() {  
        super();
    }
       
    @SuppressWarnings("unchecked")
	public BizObject loadXml( Element elt ) throws TMapDAOException {
        Annotations annotations =(Annotations)loadLinkedFile( elt );
        
        if ( annotations == null ) {

	        try {
	        	annotations =(Annotations)bof.get( BizObjectFactory.ANNOTATIONS );            
	        
		        String version =elt.getAttributeValue( "version" );   
		        
		        Attribute bgColor =elt.getAttribute( "backgroundColor" );
		        if ( bgColor != null ) {
		        	annotations.setBackgroundColor( miscHelper.rgbToColor( bgColor.getValue() ) );
		        }
		        
		        Attribute bmColor =elt.getAttribute( "baseMapColor" );
		        if ( bmColor != null ) {
		        	annotations.setBaseMapColor( miscHelper.rgbToColor( bmColor.getValue() ) );
		        }
		        
		        Attribute baseMapLineStyle =elt.getAttribute( "baseMapLineStyle" );
		        if ( baseMapLineStyle != null ) {
		        	annotations.setBaseMapLineWidth( baseMapLineStyle.getDoubleValue() );
		        }
		        
		        Element titleElt =elt.getChild( "Title" );
		        
		        annotations.setTitle( titleElt.getAttributeValue( "text" ) );
		        annotations.setTitleVisible( titleElt.getAttribute( "visible" ).getBooleanValue() );
		        annotations.setTitleStyle( getAnnotationsStyle( titleElt ) );
		        annotations.setTitlePt( getPt( titleElt ) );
		        
		        Attribute titleBg =titleElt.getAttribute( "transparentBackground" );
		        annotations.setTitleBackgroundTransparent( titleBg != null ? titleBg.getBooleanValue()
		        											     		   : false );
		        
		        // editedByUser is set to true by default
		        Attribute editedByUser =titleElt.getAttribute( "editedByUser" );
		        annotations.setTitleEditedByUser( editedByUser != null ? editedByUser.getBooleanValue() 
		        													   : true );	
		        
		        Element legendElt =elt.getChild( "Legend" );
		        
		        annotations.setLegendVisible( legendElt.getAttribute( "visible" ).getBooleanValue() );
		        annotations.setLegendStyle( getAnnotationsStyle( legendElt ) );
		        annotations.setLegendPt( getPt( legendElt ) );
		        annotations.setLegendText( legendElt.getAttributeValue( "text" ) );
		        retrieveColorPatternLegend( legendElt, annotations );
		        Attribute legendBg =legendElt.getAttribute( "transparentBackground" );
		        annotations.setLegendBackgroundTransparent( legendBg != null ? legendBg.getBooleanValue()
		        															 : false );		
		        retrieveProportionalSymbolsLegend( legendElt, annotations );
		        
		        Element labelsElt =elt.getChild( "Labels" );
		        
		        annotations.setLabelsVisible( labelsElt.getAttribute( "visible" ).getBooleanValue() );
		        annotations.setLabelsVisibleCondition( labelsElt.getAttribute( "visibleCondition" ).getDoubleValue() );
		        annotations.setLabelsVisibleIfDataExists( labelsElt.getAttribute( "visibleIfDataExists" ).getBooleanValue() );
		        annotations.setLabelsStyle( getAnnotationsStyle( labelsElt ) );
		        
		        Attribute lcnElt =labelsElt.getAttribute( "labelColumnName" );
		        if ( lcnElt != null ) {
		        	annotations.setLabelColumnName( lcnElt.getValue() );
		        }
		        
		        List<MapShape> graphics =new ArrayList<MapShape>();
		        annotations.setGraphics( graphics );
		        
		        Element graphicsElt =elt.getChild( "Graphics" );
		        Iterator it =graphicsElt.getChildren().iterator();
		        while( it.hasNext() ) {
		            Element graphicElt =(Element)it.next();
		            
		            for( int ct =0; ct < MapShapes.names.length; ct++ ) {
			            if ( graphicElt.getName().equalsIgnoreCase( MapShapes.names[ ct ] ) ) {
			                MapShapeDAO dao =(MapShapeDAO)msdf.getDAO( ct );
			                
			                MapShape shape =dao.load( graphicElt );
			                
			                graphics.add( shape );
			                break;
			            }
		            }
		        }
		        
		        Element labelsListsElt =elt.getChild( "LabelsLists" );
		        if ( labelsListsElt != null ) {
		        	LabelsListDAO llDao =(LabelsListDAO)msdf.getDAO( MapShapes.LABELS_LIST );
		        	
			        it =labelsListsElt.getChildren( "LabelsList" ).iterator();
			        while( it.hasNext() ) {
			        	Element llElt =(Element)it.next();
			        	
			        	LabelsListShape lls =(LabelsListShape)llDao.load( llElt );
			        	
			        	graphics.add( lls );
			        }	        	
		        	
		        }	        
		        
	        } catch( Exception e ) {
	            throw new TMapDAOException( e );
	        }
        }
        
        return annotations;    
    }

    /**
     * Load a set of annotations from a TMap xml file
     */
    public BizObject load( String fileName ) throws TMapDAOException {
    	BizObject object =null;
    	
    	try {
    		Element rootElt =getRootElement( fileName );
    		object =loadXml( rootElt );
    		object.setFile( new File( fileName ) );
    	} catch( Exception e ) {
    		throw new TMapDAOException( e );
    	}
    	
    	return object;
    }    
    
    
    
	public Element saveXml(BizObject object) throws TMapDAOException {
        Annotations annotations =(Annotations)object;        
        XMLOutputter outputter =getXMLOutputter();
        
        Element rootElt =new Element( ROOT_ELT );
        rootElt.setAttribute( "version", VERSION );  
        
        rootElt.setAttribute( "backgroundColor", miscHelper.colorToHexa( annotations.getBackgroundColor() ) );
        rootElt.setAttribute( "baseMapColor", miscHelper.colorToHexa( annotations.getBaseMapColor() ) ); 
        
        rootElt.setAttribute( "baseMapLineStyle", Double.valueOf( annotations.getBaseMapLineWidth() ).toString() );
        
        // Build title node
        Element titleElt =new Element( "Title" );
        rootElt.addContent( titleElt );
        
        titleElt.setAttribute( "text", annotations.getTitle() );
        titleElt.setAttribute( "visible", Boolean.valueOf( annotations.isTitleVisible() ).toString() );
        titleElt.setAttribute( "x", getX( annotations.getTitlePt() ) );
        titleElt.setAttribute( "y", getY( annotations.getTitlePt() ) );
        setAnnotationsStyle( titleElt, annotations.getTitleStyle() );
        titleElt.setAttribute( "transparentBackground", Boolean.valueOf( annotations.isTitleBackgroundTransparent() ).toString() );
        // EditedByUser set to true once saved
        titleElt.setAttribute( "editedByUser", Boolean.TRUE.toString() );
        
        // Build legend node
        Element legendElt =new Element( "Legend" );
        rootElt.addContent( legendElt );
        
        legendElt.setAttribute( "visible", Boolean.valueOf( annotations.isLegendVisible() ).toString() );
        legendElt.setAttribute( "x", getX( annotations.getLegendPt() ) ); 
        legendElt.setAttribute( "y", getY( annotations.getLegendPt() ) );
        legendElt.setAttribute( "text", annotations.getLegendText() );
        
        setAnnotationsStyle( legendElt, annotations.getLegendStyle() );
        legendElt.setAttribute( "transparentBackground", Boolean.valueOf( annotations.isLegendBackgroundTransparent() ).toString() );
        
        setColorPatternLegend( legendElt, annotations );
        setProportionalSymbolsLegend( legendElt, annotations );
        
        // Build labels node
        Element labelsElt =new Element( "Labels" );
        rootElt.addContent( labelsElt );
        labelsElt.setAttribute( "visible", Boolean.valueOf( annotations.isLabelsVisible() ).toString() );
        labelsElt.setAttribute( "visibleCondition", Double.valueOf(  annotations.getLabelsVisibleCondition() ).toString() );
        labelsElt.setAttribute( "visibleIfDataExists", Boolean.valueOf(  annotations.isLabelsVisibleIfDataExists() ).toString() );
        setAnnotationsStyle( labelsElt, annotations.getLabelsStyle() );
        if ( annotations.getLabelColumnName() != null ) {
        	labelsElt.setAttribute( "labelColumnName", annotations.getLabelColumnName() );
        }
        
        Element graphicsElt =new Element( "Graphics" );
        rootElt.addContent( graphicsElt );
        
        Element labelsListsElt =new Element( "LabelsLists" );
        rootElt.addContent( labelsListsElt );

        for( MapShape shape : annotations.getGraphics() ) {
        	
        	if ( shape instanceof AnnotationShape 
        			&& !(shape instanceof LegendShape)        			
        			&& !(shape instanceof TitleShape) 
        			&& !(shape instanceof LabelShape) ) {
				try {
					MapShapeDAO dao = (MapShapeDAO)msdf.getDAO( shape.getType() );
					if ( shape instanceof LabelsListShape ) {
						labelsListsElt.addContent( dao.save( shape ) );
					} else {
						graphicsElt.addContent( dao.save( shape ) );
					}
				} catch (TMapException e) {
					throw new TMapDAOException( e );
				}
        	} 

        }
        
        return rootElt;
	}
	
    private TextStyle getAnnotationsStyle( Element elt ) throws TMapException, DataConversionException {
    	TextStyle style =(TextStyle)bof.get( BizObjects.TEXT_STYLE );
    	
    	Element fontElt =elt.getChild( "Font" );
    	
    	style.setFontName( fontElt.getAttributeValue( "name" ) );
    	style.setFontSize( fontElt.getAttribute( "size" ).getIntValue() );
    	style.setFontStyle( fontElt.getAttribute( "style" ).getIntValue() );
    	    	
    	return style;
    }
    
    private void setAnnotationsStyle( Element elt, TextStyle style ) {
    	Element fontElt =new Element( "Font" );
    	elt.addContent( fontElt );
    	
    	fontElt.setAttribute( "name", style.getFontName() );
    	fontElt.setAttribute( "size", Integer.valueOf( style.getFontSize() ).toString() );
    	fontElt.setAttribute( "style", Integer.valueOf( style.getFontStyle() ).toString() );    	
    }
    
    private void setColorPatternLegend( Element elt, Annotations annotations ) {
    	Element colorPatternElt =new Element( "ColorPatternLegend" );
    	elt.addContent( colorPatternElt );
    	colorPatternElt.setAttribute( "roundRangesBoundaries"
    								, Boolean.valueOf( annotations.isColorPatternRoundRangesBoundaries() ).toString() );
    	
    	if ( annotations.getColorPatternLegend() != null ) {
	    	for( String legend : annotations.getColorPatternLegend() ) {
	    		Element legendElt =new Element( "Legend" );
	    		legendElt.setText( legend );
	    		colorPatternElt.addContent( legendElt );
	    	}
    	}
    }
    
    @SuppressWarnings("unchecked")
	private void retrieveColorPatternLegend( Element elt, Annotations annotations ) {
    	List<String> legends =new ArrayList<String>();
    	Element colorPatternElt =elt.getChild( "ColorPatternLegend" );
    	
    	annotations.setColorPatternRoundRangesBoundaries( false );
    	
    	if ( colorPatternElt != null ) {
    		Attribute attr =colorPatternElt.getAttribute( "roundRangesBoundaries" );
    		if ( attr != null ) {
    			try {
					annotations.setColorPatternRoundRangesBoundaries( attr.getBooleanValue() );
				} catch (DataConversionException e) {
					logger.warn( e );
				}
    		}
    		
	        Iterator it =colorPatternElt.getChildren("Legend").iterator();
	        while( it.hasNext() ) {
	            Element legendElt =(Element)it.next();
	            
	            legends.add( legendElt.getText() );
	        }
    	}
    	
    	annotations.setColorPatternLegend( legends );
       
    }
    
    private void setProportionalSymbolsLegend( Element elt, Annotations annotations ) {
    	Element psElt =new Element( "ProportionalSymbolsLegend" );
    	elt.addContent( psElt );
    	psElt.setAttribute( "roundValues"
    								, Boolean.valueOf( annotations.isProportionalSymbolsRoundValues() ).toString() );

    }    
    
    private void retrieveProportionalSymbolsLegend( Element elt, Annotations annotations ) {
    	List<String> legends =new ArrayList<String>();
    	Element psElt =elt.getChild( "ProportionalSymbolsLegend" );
    	
    	annotations.setProportionalSymbolsRoundValues( false );
    	
    	if ( psElt != null ) {
    		Attribute attr =psElt.getAttribute( "roundValues" );
    		if ( attr != null ) {
    			try {
					annotations.setProportionalSymbolsRoundValues( attr.getBooleanValue() );
				} catch (DataConversionException e) {
					logger.warn( e );
				}
    		}
    		
    	}    	
       
    }    
    
    private String getX( Point2D pt ) {
    	return pt != null ? Double.toString( pt.getX() )
    					  : "0";
    }
    
    private String getY( Point2D pt ) {
    	return pt != null ? Double.toString( -pt.getY() )
				  		  : "0";
    }    
    
    /**
     * Return coordinates if they exist
     * @param elt
     * @return
     */
    private Point2D getPt( Element elt ) {
    	Point2D pt;
    	
    	try {
    		pt =new Point2D.Double( elt.getAttribute( "x" ).getDoubleValue(), 
    								-elt.getAttribute( "y" ).getDoubleValue() );
    	} catch( Exception e ) {
    		pt =null;
    	}
        	
    	return pt;	    	
    }
	
}